const mongoose = require('mongoose')
const Schema = mongoose.Schema
const bcrypt = require('bcrypt')
const ObjectId = Schema.Types.ObjectId

// 生成salt的迭代次数
const saltRounds = 10
// 密码重试最多次数
const MAX_ATTEMPTS = 5
// 账号锁定时间
const LOCKED_TIME = 3 * 60 * 1000

const UserSchema = new Schema({
  // user admin superAdmin
  role: {
    type: String,
    default: 'user'
  },
  avatar: {
    type: String,
    default: 'https://chenjiyuan.oss-cn-beijing.aliyuncs.com/user/_/default.png'
  },
  username: {
    type: String,
    unique: true,
    trim: true,
    required: true,
    validate: [function(value) {
      // 1-15位
      return /^[\da-zA-Z_]{1,15}$/.test(value)
    }, '用户名长度限制为1-15位']
  },
  nickname: {
    type: String,
    trim: true,
    validate: [function(value) {
      // 1-10位
      return /^.{1,10}$/.test(value)
    }, '昵称长度限制为1-10位']
  },
  sex: {
    type: Number,
    default: 1
  },
  age: {
    type: Number,
    default: 0
  },
  password: {
    type: String
  },
  email: {
    type: String,
    trim: true,
    validate: [function(value) {
      return /^[A-Za-z\d]+([-_.][A-Za-z\d]+)*@([A-Za-z\d]+[-.])+[A-Za-z\d]{2,4}$/.test(value)
    }, '邮箱格式不正确']
  },
  loginAttempts: {
    type: Number,
    default: 0
  },
  lockUntil: {
    type: Number
  },
  // 账号是否被冻结
  freeze: {
    type: Boolean,
    default: false
  },
  meta: {
    createdAt: {
      type: Date,
      default: Date.now()
    },
    updatedAt: {
      type: Date,
      default: Date.now()
    }
  },
  friends: [{
    type: ObjectId,
    ref: 'User'
  }],
  socketId: {
    type: String,
    default: ''
  },
  onLine: {
    type: Boolean,
    default: false
  }
})

UserSchema.virtual('isLocked').get(function() {
  return !!(this.lockUntil && this.lockUntil > Date.now())
})

UserSchema.pre('save', function(next) {
  if (this.isNew) {
    this.meta.createdAt = this.meta.updatedAt = Date.now()
  } else {
    this.meta.updatedAt = Date.now()
  }

  next()
})

UserSchema.pre('save', function(next) {
  if (!this.isModified('password')) return next()
  if (!/^[a-zA-Z\d_]{1,16}$/.test(this.password)) {
    return next(new Error('密码只能使用字母、数字、下划线，16位以内'))
  }
  bcrypt.genSalt(saltRounds, (err, salt) => {
    if (err) return next(err)
    bcrypt.hash(this.password, salt, (error, hash) => {
      if (error) return next(error)

      this.password = hash
      next()
    })
  })
})

UserSchema.pre('save', function(next) {
  if (!this.isModified('friends')) return next()
  if (this.friends.length > 200) {
    return next(new Error('最多添加200位好友'))
  }
  next()
})

UserSchema.methods = {
  comparePassword: function(password) {
    return new Promise((resolve, reject) => {
      bcrypt.compare(password, this.password, (err, isMatch) => {
        if (err) return reject(err)
        resolve(isMatch)
      })
    })
  },
  incLoginAttempts: function(clear) {
    return new Promise((resolve, reject) => {
      if (clear) {
        this.updateOne({
          $set: {
            loginAttempts: 0
          },
          // 删除lockUntil
          $unset: {
            lockUntil: 1
          }
        }, err => {
          if (err) return reject(err)
          resolve()
        })
        return
      }
      if (this.lockUntil && this.lockUntil < Date.now()) {
        this.updateOne({
          $set: {
            loginAttempts: 1
          },
          // 删除lockUntil
          $unset: {
            lockUntil: 1
          }
        }, err => {
          if (err) return reject(err)
          resolve()
        })
      } else {
        let update = {
          $inc: {
            loginAttempts: 1
          }
        }
        if (this.loginAttempts + 1 >= MAX_ATTEMPTS) {
          update.$set = {
            lockUntil: Date.now() + LOCKED_TIME
          }
        }
        this.updateOne(update, err => {
          if (err) return reject(err)
          resolve()
        })
      }
    })
  }
}

UserSchema.set('toJSON')

mongoose.model('User', UserSchema)
